#include <iostream>
#include <string>
#include <pthread.h>
#include<cstring>
#include <unistd.h>
#include<vector>
#define NUM 5

// 等待是需要等，什么条件才会等呢？票数为0，等待之前，就要对资源的数量进行判定。
// 判定本身就是访问临界资源！，判断一定是在临界区内部的.
// 判定结果，也一定在临界资源内部。所以，条件不满足要休眠，一定是在临界区内休眠的!
// 证明一件事情：条件变量，可以允许线程等待
// 可以允许一个线程唤醒在cond等待的其他线程， 实现同步过程

int count = 1;

// 这个文件进行同步的练习，同步时用条件变量实现的
pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;  //定义一个全局的锁，用来进行互斥操作
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER; // 定义一个全局的条件变量，用来进行同步操作，同步就是为了部分线程出现饥饿问题

void* threaden(void* args)
{
    std :: string str = static_cast<char*> (args);  //强转成char类型，再用string容器接受
    while(1)  //只要对临界资源进行了访问，就说明此时我们就要进行上锁，来对临界资源进行保护了
    {
        pthread_mutex_lock(&glock);  //上锁处理
        pthread_cond_wait(&gcond,&glock); //线程进入“睡眠”状态，锁在执行这个操作以后，会自动释放掉，让线程进入等待队列中等待，等被叫醒的时候会重新上锁。
        std :: cout << str << "被唤醒了     " << count++ << std :: endl;
        pthread_mutex_unlock(&glock);  //释放锁处理
    }
}


int main()
{
    std :: vector<pthread_t> a1;
    for (int i = 0; i < NUM; i++)
    {
        char str[64];
        pthread_t s1; // 创建一个多的线程
        snprintf(str,64,"thread -%d",i);
        pthread_create(&s1, nullptr, threaden, (void*)str);
        a1.push_back(s1);
        sleep(1);
    }

    //这里可以由主线程来一次一个一个的唤醒其他的进程
    while(1)
    {
        std :: cout << "唤醒一个线程...." << std :: endl;
        pthread_cond_signal(&gcond); //这个函数的作用就是把线程给叫醒
        sleep(1);
    }

    for(int i = 0 ; i < NUM ; i++)
    {
        pthread_join(a1[i],nullptr);
    }
    //全局的锁和全局的条件变量无须自己释放，它自己会释放自己的
    return 0;
}